<?php
namespace app\service;

use think\facade\Config;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Log;

class TopviewApiService
{
    private $apiUrl;
    private $apiKey;
    private $uid;

    public function __construct()
    {
        $config = Config::get('topview');
        $this->apiUrl = $config['api_url'] ?? 'http://101.32.34.254';
        $this->apiKey = $config['api_key'] ?? '';
        $this->uid = $config['uid'] ?? '';
    }

    // AiAvatar相关
    public function getAiAvatarList($pageNo = 1, $pageSize = 20, $gender = '', $ethnicityIdList = [], $sortField = 'sort', $sortType = 'desc', $isCustom = false)
    {
        // 构建缓存键，包含所有参数
        $cacheKey = 'topview_aiavatar_list_' . $pageNo . '_' . $pageSize . '_' . $gender . '_' . md5(json_encode($ethnicityIdList)) . '_' . $sortField . '_' . $sortType . '_' . ($isCustom ? '1' : '0');
        $data = Cache::get($cacheKey);
        if ($data) {
            return $data;
        }

        // 构建查询参数
        $queryParams = [];
        $queryParams[] = 'pageNo=' . urlencode($pageNo);
        $queryParams[] = 'pageSize=' . urlencode($pageSize);
        $queryParams[] = 'sortField=' . urlencode($sortField);
        $queryParams[] = 'sortType=' . urlencode($sortType);
        $queryParams[] = 'isCustom=' . ($isCustom ? 'true' : 'false');
        
        if (!empty($gender)) {
            $queryParams[] = 'gender=' . urlencode($gender);
        }
        if (!empty($ethnicityIdList) && is_array($ethnicityIdList)) {
            foreach ($ethnicityIdList as $ethnicityId) {
                $queryParams[] = 'ethnicityIdList=' . urlencode($ethnicityId);
            }
        }

        $url = $this->apiUrl . '/v1/aiavatar/query?' . implode('&', $queryParams);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            Cache::set($cacheKey, $result['result'], 1800);
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取AiAvatar失败 - method: getAiAvatarList, url: ' . $url . ', params: ' . json_encode(compact('pageNo', 'pageSize', 'gender', 'ethnicityIdList', 'sortField', 'sortType', 'isCustom')) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '获取AiAvatar失败');
    }

    public function getVoiceList($pageNo = 1, $pageSize = 20, $language = 'zh_CN', $gender = '', $age = '', $style = '')
    {
        // 构建缓存键，包含所有参数
        $cacheKey = 'topview_voice_list_' . $pageNo . '_' . $pageSize . '_' . $language . '_' . $gender . '_' . $age . '_' . $style;
        $data = Cache::get($cacheKey);
        if ($data) {
            return $data;
        }

        // 构建查询参数
        $queryParams = [];
        $queryParams[] = 'pageNo=' . urlencode($pageNo);
        $queryParams[] = 'pageSize=' . urlencode($pageSize);
        $queryParams[] = 'language=' . urlencode($language);
        
        if (!empty($gender)) {
            $queryParams[] = 'gender=' . urlencode($gender);
        }
        if (!empty($age)) {
            $queryParams[] = 'age=' . urlencode($age);
        }
        if (!empty($style)) {
            $queryParams[] = 'style=' . urlencode($style);
        }

        $url = $this->apiUrl . '/v1/voice/query?' . implode('&', $queryParams);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            Cache::set($cacheKey, $result['result'], 1800);
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取Voice失败 - method: getVoiceList, url: ' . $url . ', params: ' . json_encode(compact('pageNo', 'pageSize', 'language', 'gender', 'age', 'style')) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '获取Voice失败');
    }

    /**
     * 提交视频头像任务
     * @param array $params
     * @return array ['taskId' => string]
     * @throws \Exception
     */
    public function submitVideoAvatarTask(array $params)
    {
        $url = $this->apiUrl . '/v1/video_avatar/task/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (!isset($result['code']) || $result['code'] != 200) {
            // 记录错误日志
            Log::error('提交Video Avatar任务失败 - method: submitVideoAvatarTask, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
            throw new \Exception($result['message'] ?? '提交Video Avatar任务失败');
        }
        // API返回空对象，我们生成一个唯一的taskId
        return [
            'taskId' => $result['result']['taskId']
        ];
    }

    public function queryVideoAvatarTask(string $taskId)
    {
        $url = $this->apiUrl . '/v1/video_avatar/task/query?needCloudFrontUrl=true&taskId=' . $taskId;
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            // // 更新数据库状态
            // $data = [
            //     'status' => $this->convertStatus($result['result']['status']),
            // ];
            
            // // 如果生成成功，保存视频地址
            // if ($result['result']['status'] === 'success') {
            //     $data['video_url'] = $result['result']['outputVideoUrl'] ?? '';
            // } else {
            //     $data['error_msg'] = $result['result']['errorMsg'] ?? '生成失败';
            // }
            
            // Db::name('video_avatar_tasks')->where('task_id', $taskId)->update($data);
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询Video Avatar任务失败 - method: queryVideoAvatarTask, url: ' . $url . ', taskId: ' . $taskId . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '查询Video Avatar任务失败');
    }

    /**
     * 转换状态
     */
    private function convertStatus($status)
    {
        $statusMap = [
            'init' => 0,    // 待处理
            'running' => 1, // 处理中
            'success' => 2,  // 已完成
            'failed' => 3      // 失败
        ];
        return $statusMap[$status] ?? 0;
    }

    public function getUploadCredential(string $format = 'mp4')
    {
        $url = $this->apiUrl . '/v1/upload/credential?format=' . urlencode($format);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取上传凭证失败 - method: getUploadCredential, url: ' . $url . ', format: ' . $format . ', response: ' . json_encode($result));
        throw new \Exception($result['error'] ?? '获取上传凭证失败');
    }

    // productAvatar相关
    public function getProductAvatarCategoryList()
    {
        $cacheKey = 'topview_product_avatar_category_list';
        $data = Cache::get($cacheKey);
        if ($data) {
            return $data;
        }

        $url = $this->apiUrl . '/v1/product_avatar/category/list';
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            Cache::set($cacheKey, $result['result'], 1800);
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取商品数字人类目失败 - method: getProductAvatarCategoryList, url: ' . $url . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '获取商品数字人类目失败');
    }

    public function getProductAvatarPublicList($pageNo = 1, $pageSize = 20, $ethnicityIds = '', $gender = '', $sortingType = '', $categoryIds = '')
    {
        // 构建缓存键，包含所有参数
        $cacheKey = 'topview_product_avatar_public_list_' . $pageNo . '_' . $pageSize . '_' . $ethnicityIds . '_' . $gender . '_' . $sortingType . '_' . $categoryIds;
        $data = Cache::get($cacheKey);
        if ($data) {
            return $data;
        }

        // 构建查询参数
        $queryParams = [];
        $queryParams[] = 'pageNo=' . urlencode($pageNo);
        $queryParams[] = 'pageSize=' . urlencode($pageSize);
        
        if (!empty($ethnicityIds)) {
            $queryParams[] = 'ethnicityIds=' . urlencode($ethnicityIds);
        }
        if (!empty($gender)) {
            $queryParams[] = 'gender=' . urlencode($gender);
        }
        if (!empty($sortingType)) {
            $queryParams[] = 'sortingType=' . urlencode($sortingType);
        }
        if (!empty($categoryIds)) {
            $queryParams[] = 'categoryIds=' . urlencode($categoryIds);
        }

        $url = $this->apiUrl . '/v1/product_avatar/public_avatar/query?' . implode('&', $queryParams);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            Cache::set($cacheKey, $result['result'], 1800);
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取公模数字人列表失败 - method: getProductAvatarPublicList, url: ' . $url . ', params: ' . json_encode(compact('pageNo', 'pageSize', 'ethnicityIds', 'gender', 'sortingType', 'categoryIds')) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '获取公模数字人列表失败');
    }

    public function submitProductAvatarReplaceImage($params)
    {
        $url = $this->apiUrl . '/v1/product_avatar/replace/submit';
        $result = $this->httpPost($url, $params);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('提交商品图片替换任务失败 - method: submitProductAvatarReplaceImage, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '提交商品图片替换任务失败');
    }

    public function queryProductAvatarReplaceImage($taskId)
    {
        $url = $this->apiUrl . '/v1/product_avatar/replace/query?taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询商品图片替换任务失败 - method: queryProductAvatarReplaceImage, url: ' . $url . ', taskId: ' . $taskId . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '查询商品图片替换任务失败');
    }

    public function submitProductAvatarImage2Video($params)
    {
        $url = $this->apiUrl . '/v1/product_avatar/image2video/submit';
        $result = $this->httpPost($url, $params);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('提交图片生成视频任务失败 - method: submitProductAvatarImage2Video, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '提交图片生成视频任务失败');
    }

    public function queryProductAvatarImage2Video($taskId)
    {
        $url = $this->apiUrl . '/v1/product_avatar/image2video/query?taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询图片生成视频任务失败 - method: queryProductAvatarImage2Video, url: ' . $url . ', taskId: ' . $taskId . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '查询图片生成视频任务失败');
    }

    /**
     * 提交通用去背景任务
     * @param array $params ['productImageFileId' => string]
     * @return array 完整API返回
     * @throws \Exception
     */
    public function submitRemoveBackgroundTask(array $params)
    {
        $url = $this->apiUrl . '/v1/common_task/remove_background/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && $result['code'] == 200) {
            return $result;
        }
        Log::error('提交去背景任务失败 - method: submitRemoveBackgroundTask, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '提交去背景任务失败');
    }

    /**
     * 查询通用去背景任务
     * @param string $taskId 任务ID
     * @param bool $needCloudFrontUrl 是否需要CloudFront加速URL
     * @return array result对象
     * @throws \Exception
     */
    public function queryRemoveBackgroundTask(string $taskId, bool $needCloudFrontUrl = false)
    {
        $url = $this->apiUrl . '/v1/common_task/remove_background/query?taskId=' . urlencode($taskId);
        if ($needCloudFrontUrl) {
            $url .= '&needCloudFrontUrl=true';
        }
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        Log::error('查询去背景任务失败 - method: queryRemoveBackgroundTask, url: ' . $url . ', taskId: ' . $taskId . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '查询去背景任务失败');
    }

    // avatar marketing video 相关
    /**
     * 提交avatar marketing video任务
     * @param array $params
     * @return mixed
     * @throws \Exception
     */
    public function submitAvatarMarketingVideoTask(array $params)
    {
        $url = $this->apiUrl . '/v1/avatar_marketing_video/submit';
        $result = $this->httpPost($url, $params);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('提交avatar marketing video任务失败 - method: submitAvatarMarketingVideoTask, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '提交avatar marketing video任务失败');
    }

    /**
     * 查询avatar marketing video任务
     * @param string $taskId
     * @return mixed
     * @throws \Exception
     */
    public function queryAvatarMarketingVideoTask(string $taskId)
    {
        $url = $this->apiUrl . '/v1/avatar_marketing_video/query?taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询avatar marketing video任务失败 - method: queryAvatarMarketingVideoTask, url: ' . $url . ', taskId: ' . $taskId . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '查询avatar marketing video任务失败');
    }

    /**
     * 获取avatar marketing video脚本内容
     * @param array $params
     * @return mixed
     * @throws \Exception
     */
    public function getAvatarMarketingVideoScript(array $params)
    {
        $url = $this->apiUrl . '/v1/avatar_marketing_video/script/list';
        $result = $this->httpPost($url, $params);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取avatar marketing video脚本失败 - method: getAvatarMarketingVideoScript, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '获取avatar marketing video脚本失败');
    }

    /**
     * 更新avatar marketing video脚本内容
     * @param array $params
     * @return mixed
     * @throws \Exception
     */
    public function updateAvatarMarketingVideoScript(array $params)
    {
        $url = $this->apiUrl . '/v1/avatar_marketing_video/script/update';
        $result = $this->httpPost($url, $params);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('更新avatar marketing video脚本失败 - method: updateAvatarMarketingVideoScript, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '更新avatar marketing video脚本失败');
    }

    /**
     * 获取营销数字人列表
     * @param array $params 查询参数
     * @return array
     * @throws \Exception
     */
    public function getMarketingAvatarList(array $params = [])
    {
        $queryParams = [];
        
        // 添加查询参数
        if (!empty($params['ethnicityIds'])) {
            $queryParams[] = 'ethnicityIds=' . urlencode($params['ethnicityIds']);
        }
        if (!empty($params['gender'])) {
            $queryParams[] = 'gender=' . urlencode($params['gender']);
        }
        if (!empty($params['sortingType'])) {
            $queryParams[] = 'sortingType=' . urlencode($params['sortingType']);
        }
        if (!empty($params['categoryIds'])) {
            $queryParams[] = 'categoryIds=' . urlencode($params['categoryIds']);
        }
        if (!empty($params['pageNo'])) {
            $queryParams[] = 'pageNo=' . urlencode($params['pageNo']);
        }
        if (!empty($params['pageSize'])) {
            $queryParams[] = 'pageSize=' . urlencode($params['pageSize']);
        }

        $url = $this->apiUrl . '/v1/avatar_marketing_video/avatar/list';
        if (!empty($queryParams)) {
            $url .= '?' . implode('&', $queryParams);
        }
        var_dump($url);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取营销数字人列表失败 - method: getMarketingAvatarList, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '获取营销数字人列表失败');
    }

    // 内部请求方法
    private function httpGet($url)
    {
        $headers = [
            'Authorization: Bearer ' . $this->apiKey,
            'Topview-Uid: ' . $this->uid,
            'Content-Type: application/json',
        ];
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        $response = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);
        if ($error) {
            // 记录CURL错误日志
            Log::error('CURL请求失败 - method: httpGet, url: ' . $url . ', error: ' . $error);
            throw new \Exception('CURL错误：' . $error);
        }
        return json_decode($response, true);
    }

    private function httpPost($url, $data)
    {
        $headers = [
            'Authorization: Bearer ' . $this->apiKey,
            'Topview-Uid: ' . $this->uid,
            'Content-Type: application/json',
        ];
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        $response = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);
        if ($error) {
            // 记录CURL错误日志
            Log::error('CURL请求失败 - method: httpPost, url: ' . $url . ', data: ' . json_encode($data) . ', error: ' . $error);
            throw new \Exception('CURL错误：' . $error);
        }
        return json_decode($response, true);
    }

    /**
     * 提交声音克隆任务
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function submitVoiceCloneTask(array $params)
    {
        $url = $this->apiUrl . '/v1/voice/clone/task/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        Log::error('提交声音克隆任务失败 - method: submitVoiceCloneTask, url: ' . $url . ', params: ' . json_encode($params) . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '提交声音克隆任务失败');
    }

    /**
     * 查询声音克隆任务
     * @param string $taskId
     * @return array
     * @throws \Exception
     */
    public function queryVoiceCloneTask(string $taskId)
    {
        $url = $this->apiUrl . '/v1/voice/clone/task/query?taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        Log::error('查询声音克隆任务失败 - method: queryVoiceCloneTask, url: ' . $url . ', taskId: ' . $taskId . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '查询声音克隆任务失败');
    }

    /**
     * 提交文字转语音任务
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function submitText2VoiceTask(array $params)
    {
        $url = $this->apiUrl . '/v1/voice/text2voice/task/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && (string)$result['code'] === '200') {
            return $result['result'] ?? $result; // 兼容有/无result包裹
        }
        Log::error('提交文字转语音任务失败 - 
            params: ' . json_encode($params) . ',
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '提交文字转语音任务失败');
    }

    /**
     * 查询文字转语音任务
     * @param string $taskId
     * @return array
     * @throws \Exception
     */
    public function queryText2VoiceTask(string $taskId)
    {
        $url = $this->apiUrl . '/v1/voice/text2voice/task/query?needCloudFrontUrl=true&taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        if (isset($result['code']) && (string)$result['code'] === '200') {
            return $result['result'] ?? $result;
        }
        Log::error('查询文字转语音任务失败 - 
            taskId: $taskId,
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '查询文字转语音任务失败');
    }

    public function getAudioList($pageNo = 1, $pageSize = 10)
    {
        $cacheKey = 'topview_audio_list_' . $pageNo . '_' . $pageSize;
        $data = Cache::get($cacheKey);
        if ($data) {
            return $data;
        }
        $url = $this->apiUrl . '/v1/audio/query?pageNo=' . urlencode($pageNo) . '&pageSize=' . urlencode($pageSize);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            Cache::set($cacheKey, $result['result'], 1800);
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取音频列表失败 - 
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '获取音频列表失败');
    }

    /**
     * 查询图片替换任务结果
     * @param string $taskId 任务ID
     * @return array
     */
    /**
     * 查询图片替换任务结果
     * @param string $taskId 任务ID
     * @return array 返回完整的API响应，包含以下结构：
     * [
     *     'code' => 'string',           // 响应状态码
     *     'message' => 'string',        // 响应消息
     *     'result' => [
     *         'taskId' => 'string',     // 任务ID
     *         'status' => 'string',     // 任务状态
     *         'replaceProductResult' => [  // 图片替换结果数组
     *             [
     *                 'imageId' => 'string',    // 图片ID
     *                 'faceExistence' => bool,  // 是否检测到人脸
     *                 'url' => 'string'         // 替换后的图片URL
     *             ]
     *         ]
     *     ]
     * ]
     * @throws \Exception
     */
    public function getImageReplaceResult($taskId)
    {
        $url = $this->apiUrl . '/v2/product_avatar/task/image_replace/query?needCloudFrontUrl=true&taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result;
        }
        // 记录错误日志
        Log::error('查询图片替换任务结果失败 - method: getImageReplaceResult, url: ' . $url . ', taskId: ' . $taskId . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '查询图片替换任务结果失败');
    }

    /**
     * 提交图片替换任务
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function submitImageReplaceTask(array $params)
    {
        $url = $this->apiUrl . '/v2/product_avatar/task/image_replace/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && $result['code'] == 200) {
            return $result;
        }
        // 记录错误日志
        Log::error('提交图片替换任务失败 - 
            params: ' . json_encode($params) . ',
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '提交图片替换任务失败');
    }

    /**
     * 提交图片转视频任务
     * @param array $params [
     *     'productReplaceResultKey' => string, // 图片替换任务的结果key
     *     'mode' => string,         // lite/pro
     *     'image2videoPrompt' => string,
     *     'voiceoverId' => string,
     *     'ttsText' => string
     * ]
     * @return array
     * @throws \Exception
     */
    public function submitImage2VideoTask(array $params)
    {
        $url = $this->apiUrl . '/v2/product_avatar/task/image2Video/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('提交图片转视频任务失败 -
            params: ' . json_encode($params) . ',
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '提交图片转视频任务失败');
    }

    /**
     * 查询图片转视频任务结果
     */
    public function queryImage2VideoTask($taskId)
    {
        $url = $this->apiUrl . '/v2/product_avatar/task/image2Video/query?needCloudFrontUrl=true&taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        var_dump($result);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询图片转视频任务失败 - 
            taskId: $taskId,
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '查询图片转视频任务失败');
    }

    /**
     * 提交M2V任务
     * @param array $params [
     *     'productLink' => string,        // 可选，产品链接
     *     'fileIds' => array,             // 可选，自定义素材文件ID数组
     *     'productName' => string,        // 可选，产品名称
     *     'productDescription' => string, // 可选，产品描述
     *     'aspectRatio' => string,        // 可选，期望输出的分辨率，默认9:16
     *     'language' => string,           // 可选，语言，默认en
     *     'voiceId' => string,           // 可选，音色id
     *     'captionId' => string,         // 可选，字幕样式id
     *     'aiavatarId' => string,        // 可选，数字人id
     *     'videoLengthType' => int,      // 可选，输出视频长度类型，默认1
     *     'endcardFileId' => string,     // 可选，片尾文件id
     *     'endcardAspectRatio' => string,// 可选，片尾文件期望的分辨率
     *     'endcardBackgroundColor' => string, // 可选，片尾背景颜色
     *     'logoFileId' => string,        // 可选，logo文件id
     *     'preview' => bool,             // 可选，是否预览模式，默认false
     *     'noticeUrl' => string          // 可选，回调地址
     * ]
     * @return array
     * @throws \Exception
     */
    public function submitM2vTask(array $params)
    {
        $url = $this->apiUrl . '/v1/m2v/task/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('提交M2V任务失败 - 
            params: ' . json_encode($params) . ',
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '提交M2V任务失败');
    }

    /**
     * 查询M2V任务状态
     * @param string $taskId 任务ID
     * @return array
     * @throws \Exception
     */
    public function queryM2vTask(string $taskId)
    {
        $url = $this->apiUrl . '/v1/m2v/task/query?needCloudFrontUrl=true&taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询M2V任务失败 - 
            taskId: $taskId,
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '查询M2V任务失败');
    }

    /**
     * 获取人种列表
     * @param string $scene 使用场景 video_avatar|product_avatar
     * @param int $pageNo 页码
     * @param int $pageSize 每页条数
     * @return array
     * @throws \Exception
     */
    public function getEthnicityList(string $scene, int $pageNo = 1, int $pageSize = 10)
    {
        // 缓存键包含所有参数
        $cacheKey = 'topview_ethnicity_list_' . $scene . '_' . $pageNo . '_' . $pageSize;
        $data = Cache::get($cacheKey);
        if ($data) {
            return $data;
        }

        $queryParams = [];
        $queryParams[] = 'scene=' . urlencode($scene);
        if ($pageNo) {
            $queryParams[] = 'pageNo=' . urlencode($pageNo);
        }
        if ($pageSize) {
            $queryParams[] = 'pageSize=' . urlencode($pageSize);
        }

        $url = $this->apiUrl . '/v1/ethnicity/list?' . implode('&', $queryParams);
        $result = $this->httpGet($url);
        
        if (isset($result['code']) && $result['code'] == 200) {
            // 缓存30分钟
            Cache::set($cacheKey, $result['result'], 1800);
            return $result['result'];
        }
        
        // 记录错误日志
        Log::error('获取人种列表失败 - 
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '获取人种列表失败');
    }

    // Product AnyShoot相关方法

    /**
     * 获取Product AnyShoot模板类目列表
     * @return array
     * @throws \Exception
     */
    public function getProductAnyShootTemplateCategoryList()
    {
        $url = $this->apiUrl . '/v1/product_anyShoot/template/category/list';
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取Product AnyShoot模板类目失败 - 
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '获取Product AnyShoot模板类目失败');
    }

    /**
     * 获取Product AnyShoot模板列表
     * @param string $categoryIds 类目ID，多个类目以逗号分隔
     * @param string $style UGC/Pro
     * @param int $pageNo 页码
     * @param int $pageSize 每页条数
     * @return array
     * @throws \Exception
     */
    public function getProductAnyShootTemplateList($categoryIds = '', $style = '', $pageNo = 1, $pageSize = 20)
    {
        // 构建缓存键
        $cacheKey = 'product_anyshoot_template_list_' . md5($categoryIds . '_' . $style . '_' . $pageNo . '_' . $pageSize);
        $data = Cache::get($cacheKey);
        if ($data) {
            return $data;
        }

        // 构建查询参数
        $queryParams = [];
        $queryParams[] = 'pageNo=' . urlencode($pageNo);
        $queryParams[] = 'pageSize=' . urlencode($pageSize);
        
        // categoryIds即使为空也要传递
        $queryParams[] = 'categoryIds=' . urlencode($categoryIds);
        
        if (!empty($style)) {
            $queryParams[] = 'style=' . urlencode($style);
        }

        $url = $this->apiUrl . '/v1/product_anyShoot/template/list?' . implode('&', $queryParams);
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            // 缓存30分钟
            Cache::set($cacheKey, $result['result'], 1800);
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取Product AnyShoot模板列表失败 - 
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '获取Product AnyShoot模板列表失败');
    }

    /**
     * 提交Product AnyShoot替换商品图片任务
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function submitProductAnyShootReplaceImageTask(array $params)
    {
        $url = $this->apiUrl . '/v2/product_anyShoot/product_model/task/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('提交Product AnyShoot替换商品图片任务失败 - 
            params: ' . json_encode($params) . ',
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '提交Product AnyShoot替换商品图片任务失败');
    }

    /**
     * 查询Product AnyShoot替换商品图片任务
     * @param string $taskId
     * @return array
     * @throws \Exception
     */
    public function queryProductAnyShootReplaceImageTask($taskId)
    {
        $url = $this->apiUrl . '/v2/product_anyShoot/product_model/task/result?needCloudFrontUrl=true&taskId=' . urlencode($taskId);
        $result = $this->httpGet($url);
        var_dump($result);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询Product AnyShoot替换商品图片任务失败 - 
            taskId: $taskId,
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '查询Product AnyShoot替换商品图片任务失败');
    }

    /**
     * 通用图片转视频任务提交
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function submitCommonImage2VideoTask(array $params)
    {
        $url = $this->apiUrl . '/v1/common_task/image2video/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('提交通用图片转视频任务失败 - 
            params: ' . json_encode($params) . ',
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '提交通用图片转视频任务失败');
    }

    /**
     * 查询通用图片转视频任务结果
     * @param string $taskId
     * @param bool $needCloudFrontUrl
     * @return array
     * @throws \Exception
     */
    public function queryCommonImage2VideoTask($taskId, $needCloudFrontUrl = false)
    {
        $url = $this->apiUrl . '/v1/common_task/image2video/result?needCloudFrontUrl=true&taskId=' . urlencode($taskId);
        if ($needCloudFrontUrl) {
            $url .= '&needCloudFrontUrl=true';
        }
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('查询通用图片转视频任务失败 - 
            taskId: $taskId,
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '查询通用图片转视频任务失败');
    }

    /**
     * v2 Product AnyShoot 背景替换 - 提交任务
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function submitProductBackgroundTask(array $params)
    {
        // 文档: /v2/product_anyShoot/product_background/task/submit
        $url = $this->apiUrl . '/v2/product_anyShoot/product_background/task/submit';
        $result = $this->httpPost($url, json_encode($params));
        if (isset($result['code']) && (string)$result['code'] === '200') {
            // 兼容 result 包裹与否
            return $result['result'] ?? $result;
        }
        Log::error('提交 ProductAnyShoot 背景任务失败 - 
            params: ' . json_encode($params) . ',
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '提交 ProductAnyShoot 背景任务失败');
    }

    /**
     * v2 Product AnyShoot 背景替换 - 查询任务
     * @param string $taskId
     * @param bool $needCloudFrontUrl
     * @return array
     * @throws \Exception
     */
    public function queryProductBackgroundTask(string $taskId, bool $needCloudFrontUrl = false)
    {
        // 文档: /v2/product_anyShoot/product_background/task/query
        $url = $this->apiUrl . '/v2/product_anyShoot/product_background/task/query?taskId=' . urlencode($taskId);
        if ($needCloudFrontUrl) {
            $url .= '&needCloudFrontUrl=true';
        }
        $result = $this->httpGet($url);
        if (isset($result['code']) && (string)$result['code'] === '200') {
            return $result['result'] ?? $result;
        }
        Log::error('查询 ProductAnyShoot 背景任务失败 - 
            taskId: $taskId,
            response: ' . json_encode($result)
        );
        throw new \Exception($result['message'] ?? '查询 ProductAnyShoot 背景任务失败');
    }

    /**
     * v2 Product AnyShoot 背景模板列表
     * @param array $params [categoryId, isCustom, pageNo, pageSize]
     * @return array
     * @throws \Exception
     */
    public function getProductBackgroundTemplateList(array $params = [])
    {
        // 文档: /v2/product_anyShoot/product_background/template/list
        $query = [];
        if (isset($params['categoryId'])) {
            $query[] = 'categoryId=' . urlencode($params['categoryId']);
        }
        if (isset($params['isCustom'])) {
            $query[] = 'isCustom=' . ($params['isCustom'] ? 'true' : 'false');
        }
        if (isset($params['pageNo'])) {
            $query[] = 'pageNo=' . urlencode($params['pageNo']);
        }
        if (isset($params['pageSize'])) {
            $query[] = 'pageSize=' . urlencode($params['pageSize']);
        }
        $url = $this->apiUrl . '/v2/product_anyShoot/product_background/template/list' . (empty($query) ? '' : ('?' . implode('&', $query)));
        $result = $this->httpGet($url);
        if (isset($result['code']) && (string)$result['code'] === '200') {
            return $result['result'] ?? $result;
        }
        $errorMsg = $result['message'] ?? '获取 ProductAnyShoot 背景模板列表失败';
        Log::error('获取 ProductAnyShoot 背景模板列表失败 - 
            response: ' . json_encode($result)
        );
        throw new \Exception($errorMsg . ' - URL: ' . $url . ' - Response: ' . json_encode($result, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 获取字幕样式列表
     * @return array
     * @throws \Exception
     */
    public function getCaptionList()
    {
        $url = $this->apiUrl . '/v1/caption/list';
        $result = $this->httpGet($url);
        if (isset($result['code']) && $result['code'] == 200) {
            return $result['result'];
        }
        // 记录错误日志
        Log::error('获取字幕样式列表失败 - method: getCaptionList, url: ' . $url . ', response: ' . json_encode($result));
        throw new \Exception($result['message'] ?? '获取字幕样式列表失败');
    }
}